//-----------------------------------------------------------------------
//  This file is part of the Microsoft Robotics Studio Code Samples.
//
//  Copyright (C) Microsoft Corporation.  All rights reserved.
//
//  Updated Nov-2007 by Trevor Taylor, Software Technology, Australia
//
//  Change Log:
//  Added new service operations for setting I/O pins, playing a tone
//  on the speaker and executing pre-defined behaviors (on the robot).
//
//  $File: BasicStamp2Types.cs $ $Revision: 16 $
//-----------------------------------------------------------------------

using Microsoft.Ccr.Core;
using Microsoft.Dss.Core.Attributes;
using Microsoft.Dss.ServiceModel.Dssp;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using W3C.Soap;
using Microsoft.Dss.Core.DsspHttp;

using bs2 = Microsoft.Robotics.Services.BasicStamp2;

namespace Microsoft.Robotics.Services.BasicStamp2
{
    
    public static class Contract
    {
        // TT - Modified the Contract Identifier for the new version
        public const string Identifier = "http://schemas.microsoft.com/robotics/2007/10/basicstamp2.html";
    }

    // Replaced for CF compatibility
    //[ServicePort]
    //public class BasicStamp2Operations : PortSet<
    //    DsspDefaultLookup,
    //    DsspDefaultDrop,
    //    Get,
    //    Replace,
    //    Subscribe,
    //    QueryConfig,
    //    QueryInfraRed,
    //    QueryMotorSpeed,
    //    UpdateConfig,
    //    UpdateMotorSpeed,
    //    SensorsChanged,
    //    UpdateAutonomousMode,
    //    HttpGet,
    //    HttpPost,
    //    // TT Oct-2007 - Additional operations that were not supported
    //    SetPins,
    //    PlayTone,
    //    ExecuteBehavior
    //    >
    //{
    //}


    [ServicePort]
    public class BasicStamp2Operations : PortSet
    {
        public BasicStamp2Operations()
            : base(
            typeof(DsspDefaultLookup),
            typeof(DsspDefaultDrop),
            typeof(Get),
            typeof(Replace),
            typeof(Subscribe),
            typeof(QueryConfig),
            typeof(QueryInfraRed),
            typeof(QueryMotorSpeed),
            typeof(UpdateConfig),
            typeof(UpdateMotorSpeed),
            typeof(SensorsChanged),
            typeof(UpdateAutonomousMode),
            typeof(HttpGet),
            typeof(HttpPost),
            typeof(SetPins),
            typeof(PlayTone),
            typeof(ExecuteBehavior)
            )
        { }

        public void Post(DsspDefaultDrop msg)
        {
            base.PostUnknownType(msg);
        }

        public void Post(SensorsChanged msg)
        {
            base.PostUnknownType(msg);
        }

        public void Post(UpdateMotorSpeed msg)
        {
            base.PostUnknownType(msg);
        }
    }


    #region Data Contracts
    [Description("The BASIC Stamp's configuration state.")]
    [DataContract]
    public class BasicStampState
    {
        // inbound control
        private Config _config;
        private bool _autonomousMode;
        private MotorSpeed _motorSpeed;

        // outbound readings
        private bool _connected;
        private int _connectAttempts;
        private int _frameCounter;
        private Sensors _infraRed;

        [Description("Configuration details.")]
        [DataMember]
        public Config Configuration
        {
            get { return this._config; }
            set { this._config = value; }
        }

        [Browsable(false)]
        [DataMember]
        [Description("Specifies if the BASIC Stamp is in autonomous mode or if it is controlled by the PC.")]
        public bool AutonomousMode
        {
            get { return this._autonomousMode; }
            set { this._autonomousMode = value; }
        }

        [Browsable(false)]
        [DataMember]
        [Description("Specifies if the BASIC Stamp is currently connected.")]
        public bool Connected
        {
            get { return this._connected; }
            set { this._connected = value; }
        }

        [Browsable(false)]
        [DataMember]
        [Description("Specifies the current frame count.")]
        public int FrameCounter
        {
            get { return this._frameCounter; }
            set { this._frameCounter = value; }
        }

        [Browsable(false)]
        [DataMember]
        [Description("Specifies how many connection attempts were made.")]
        public int ConnectAttempts
        {
            get { return _connectAttempts; }
            set { _connectAttempts = value; }
        }

        [Browsable(false)]
        [DataMember]
        [Description("Specifies the current motor speed setting.")]
        public MotorSpeed MotorSpeed
        {
            get { return this._motorSpeed; }
            set { this._motorSpeed = value; }
        }

        [Browsable(false)]
        [DataMember]
        [Description("Specifies the state of the BASIC Stamp's set of sensors.")]
        public Sensors Sensors
        {
            get { return this._infraRed; }
            set { this._infraRed = value; }
        }

    }


    [Description("Specifies the state of the BASIC Stamp's set of sensors.")]
    [DataContract]
    public class Sensors
    {
        private bool _whiskerLeft;
        private bool _whiskerRight;
        private bool _irLeft;
        private bool _irRight;


        [Browsable(false)]
        [DataMember, DataMemberConstructor(Order = 1)]
        [Description("Identifies the left IR sensor's value.")]
        public bool IRLeft
        {
            get { return this._irLeft; }
            set { this._irLeft = value; }
        }

        [Browsable(false)]
        [DataMember, DataMemberConstructor(Order = 2)]
        [Description("Identifies the right IR sensor's value.")]
        public bool IRRight
        {
            get { return this._irRight; }
            set { this._irRight = value; }
        }

        [Browsable(false)]
        [DataMember, DataMemberConstructor(Order = 3)]
        [Description("Identifies the left whisker's value.")]
        public bool WhiskerLeft
        {
            get { return this._whiskerLeft; }
            set { this._whiskerLeft = value; }
        }

        [Browsable(false)]
        [DataMember, DataMemberConstructor(Order = 4)]
        [Description("Identifies the right whisker's value.")]
        public bool WhiskerRight
        {
            get { return this._whiskerRight; }
            set { this._whiskerRight = value; }
        }
    }

    [Description("Specifies the current left and right motor speed.")] 
    [DataContract]
    public class MotorSpeed
    {
        private double? _leftSpeed;
        private double? _rightSpeed;

        [Browsable(false)]
        [DataMember, DataMemberConstructor(Order = 1)]
        [Description("Identifies the left motor's speed setting.")]
        public double? LeftSpeed
        {
            get { return this._leftSpeed; }
            set { this._leftSpeed = value; }
        }

        [Browsable(false)]
        [DataMember, DataMemberConstructor(Order = 2)]
        [Description("Identifies the right motor's speed setting.")]
        public double? RightSpeed
        {
            get { return this._rightSpeed; }
            set { this._rightSpeed = value; }
        }

    }

    /////////////////////////////////////////////////////////////////
    //++
    // TT Oct-2007 - Add request to Set Pins
    [Description("Specifies settings for up to two I/O pins.")]
    [DataContract]
    public class Pins
    {
        // Pins
        private byte _pin1;
        private byte _pin2;
        // Operations
        private byte _op1;
        private byte _op2;

        [DataMember, DataMemberConstructor(Order = 1)]
        [Description("Number of the first pin.")]
        public byte Pin1
        {
            get { return this._pin1; }
            set { this._pin1 = value; }
        }

        [DataMember, DataMemberConstructor(Order = 2)]
        [Description("Operation for first pin.")]
        public byte Op1
        {
            get { return this._op1; }
            set { this._op1 = value; }
        }

        [DataMember, DataMemberConstructor(Order = 3)]
        [Description("Number of the second pin.")]
        public byte Pin2
        {
            get { return this._pin2; }
            set { this._pin2 = value; }
        }

        [DataMember, DataMemberConstructor(Order = 4)]
        [Description("Operation for second pin.")]
        public byte Op2
        {
            get { return this._op2; }
            set { this._op2 = value; }
        }
    }

    // TT Oct-2007 - Add request to Sound a Tone on the Speaker
    [Description("Sound a Tone on the Speaker.")]
    [DataContract]
    public class Tone
    {
        // Parameters
        private int _frequency;
        private int _duration;

        [DataMember, DataMemberConstructor(Order = 1)]
        [Description("Frequency in Hz (rounded to nearest 50Hz).")]
        public int Frequency
        {
            get { return this._frequency; }
            set { this._frequency = value; }
        }

        [DataMember, DataMemberConstructor(Order = 2)]
        [Description("Duration in Milliseconds (rounded to nearest 50ms).")]
        public int Duration
        {
            get { return this._duration; }
            set { this._duration = value; }
        }
    }

    // TT Oct-2007 - Add request to Execute a Behavior
    // Behaviors are predefined in the monitor program that runs on the robot.
    // They can do a variety of different things. However, be aware that
    // while a behavior is executing, the robot will be unresponsive to
    // its sensors and any requests from the controlling PC.
    // This approach is easily extensible.
    [Description("Execute pre-defined Behavior (in firmware).")]
    [DataContract]
    public class Behavior
    {
        // Parameters
        private char _behavior;
        private int _duration;

        [Browsable(false)]
        [DataMember, DataMemberConstructor(Order = 1)]
        [Description("Code (single character code for the Behavior).")]
        public char Code
        {
            get { return this._behavior; }
            set { this._behavior = value; }
        }

        [Browsable(false)]
        [DataMember, DataMemberConstructor(Order = 2)]
        [Description("Duration counter (multiple of 23ms).")]
        public int Duration
        {
            get { return this._duration; }
            set { this._duration = value; }
        }
    }
    //--
    /////////////////////////////////////////////////////////////////


    [Description("The BASIC Stamp's low level configuration parameters.")]
    [DataContract]
    public class Config
    {
        private int _delay;
        private int _serialPort;

        [Description("Specifies the delay (in ms) for accessing the serial port.")]
        [DataMember]
        public int Delay
        {
            get { return this._delay; }
            set { this._delay = value; }
        }

        [Description("Specifies the serial port number used for connecting to the BASIC Stamp.")]
        [DataMember]
        public int SerialPort
        {
            get { return this._serialPort; }
            set { this._serialPort = value; }
        }

    }

    [Description("Specifies if the BASIC Stamp is in autonomous mode or if it is controlled by the PC.")]
    [DataContract]
    public class AutonomousMode
    {
        [Description("Identifies the current operational mode (true = autonomous mode).")]
        [DataMember]
        public bool Mode;
    }

    #endregion

    #region Operation Ports
    [DisplayName("Get")]
    [Description("Gets the Boe-Bot's current state.")]
    public class Get : Get<GetRequestType, PortSet<BasicStampState, Fault>>
    {
    }

    [DisplayName("GetConfiguration")]
    [Description("Gets the Boe-Bot's current configuration.")]
    public class QueryConfig : Query<Config, PortSet<Config, Fault>>
    {
    }

    [DisplayName("GetSensors")]
    [Description("Gets the infrared and whisker sensors' state.")]
    public class QueryInfraRed : Query<Sensors, PortSet<Sensors, Fault>>
    {
    }

    [DisplayName("GetMotorSpeed")]
    [Description("Gets the motor speed.")]
    public class QueryMotorSpeed : Query<MotorSpeed, PortSet<MotorSpeed, Fault>>
    {
    }

    [DisplayName("UpdateConfiguration")]
    [Description("Updates (or indicates an update to) the Boe-Bot's configuration.")]
    public class UpdateConfig : Update<Config, PortSet<DefaultUpdateResponseType, Fault>>
    {
    }

    [DisplayName("UpdateSpeed")]
    [Description("Updates (or indicates an update to) motor speed.")]
    public class UpdateMotorSpeed : Update<MotorSpeed, PortSet<DefaultUpdateResponseType, Fault>>
    {
        public UpdateMotorSpeed() 
        {
            if (this.Body == null)
                this.Body = new MotorSpeed();
        }
        public UpdateMotorSpeed(MotorSpeed motorSpeed) 
        {
            this.Body = motorSpeed;
        }
    }

    [DisplayName("SensorsChanged")]
    [Description("Indicates one or more of the Boe-Bot's whiskers or infrared sensors have changed.")]
    public class SensorsChanged : Update<Sensors, PortSet<DefaultUpdateResponseType, Fault>>
    {
        public SensorsChanged() 
        {
            if (this.Body == null)
                this.Body = new Sensors();
        }
        public SensorsChanged(Sensors infraRed) 
        {
            this.Body = infraRed;
        }
    }

    [DisplayName("UpdateAutonomousMode")]
    [Description("Updates (or indicates an update to) the Boe-Bot's autonomous state.")]
    public class UpdateAutonomousMode : Update<AutonomousMode, PortSet<DefaultUpdateResponseType, Fault>>
    {
    }

    [DisplayName("ChangeState")]
    [Description("Changes (or indicates a change to) the Boe-Bot's entire state.")]
    public class Replace : Replace<BasicStampState, PortSet<DefaultReplaceResponseType, Fault>>
    {
    }

    /////////////////////////////////////////////////////////////////
    //++
    // TT Oct-2007 - Set Pins Port
    [DisplayName("SetPins")]
    [Description("Sets Digital I/O Pins on the Boe-Bot.")]
    public class SetPins : Update<Pins, PortSet<DefaultUpdateResponseType, Fault>>
    {
    }

    // The supported operations for Pins
    // NOTE: Even though you can set a pin to Input mode, there is currently
    // no operation defined to read pins. Maybe in the next version ...
    [DataContract]
    public enum PinOperations { NO_OP = 0, SET_OUTPUT, SET_INPUT, OUTPUT_HIGH, OUTPUT_LOW }

    // TT Oct-2007 - Play Tone Port
    [DisplayName("PlayTone")]
    [Description("Plays a tone on the speaker on the Boe-Bot.")]
    public class PlayTone : Update<Tone, PortSet<DefaultUpdateResponseType, Fault>>
    {
    }

    // TT Oct-2007 - Execute Behavior
    [DisplayName("ExecuteBehavior")]
    [Description("Executes a firmware behavior on the Boe-Bot.")]
    public class ExecuteBehavior : Update<Behavior, PortSet<DefaultUpdateResponseType, Fault>>
    {
    }

    // The supported operations for Behaviors
    // To add a new behavior, pick a letter that is not used and then
    // add the appropriate code to the handler. You will have to write
    // the necessary code in PBASIC on the BASICStamp as well. The
    // BASICStamp is a very primitive processor, so it cannot do more
    // than one task at a time. Be careful with behaviors that they do
    // not endanger the robot by running around blindly.
    // NOTE: The Turn, Forward and Backward behaviors are intended to
    // be used with the RotateDegrees and DriveDistance operations of
    // the differential drive service.
    [DataContract]
    public enum Behaviors { U_TURN = 'U', LEFT_TURN = 'L', RIGHT_TURN = 'R',
        FORWARD = 'F', BACKWARD = 'B', CLOCKWISE = 'C', ANTI_CLOCKWISE = 'A' }

    //--
    /////////////////////////////////////////////////////////////////

    public class Subscribe : Subscribe<SubscribeRequestType, PortSet<SubscribeResponseType, Fault>>
    {
    }
    #endregion
}
